"""
持仓计算服务
============

基于现有的OmsEngine设计的实时持仓计算服务
支持实时持仓计算、PnL计算、风险指标计算
"""

# 确保项目根目录在Python路径中

# 统一路径管理
from backend.shared import PathManager
PathManager().setup_paths()
import sys
import os

import asyncio
import logging
from typing import Dict, List, Optional, Any, Tuple
from decimal import Decimal, ROUND_HALF_UP
from datetime import datetime, timedelta
from dataclasses import dataclass, field
from enum import Enum
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select, and_, or_, update, func

from ..models.trading_models import (
    TradingPosition, TradingTrade, TradingOrder, TradingAccount,
    TradingContract, PositionDirection, TradeDirection, OrderDirection
)
from ..core.exceptions import PositionNotFoundError, ContractNotFoundError

class PnLType(str, Enum):
    """盈亏类型"""
    REALIZED = "realized"    # 已实现盈亏
    UNREALIZED = "unrealized"  # 未实现盈亏
    TOTAL = "total"         # 总盈亏

@dataclass
class PositionData:
    """持仓数据"""
    user_id: str
    symbol: str
    exchange: str
    direction: PositionDirection
    
    # 持仓数量
    volume: Decimal = Decimal('0')
    frozen: Decimal = Decimal('0')
    available: Decimal = Decimal('0')
    
    # 成本信息
    average_price: Decimal = Decimal('0')
    total_cost: Decimal = Decimal('0')
    
    # 昨仓信息
    yd_volume: Decimal = Decimal('0')
    yd_price: Decimal = Decimal('0')
    
    # 盈亏信息
    market_price: Decimal = Decimal('0')
    market_value: Decimal = Decimal('0')
    pnl: Decimal = Decimal('0')
    pnl_ratio: Decimal = Decimal('0')
    
    # 风险指标
    margin_ratio: Decimal = Decimal('0')
    risk_degree: str = "LOW"
    
    # 时间信息
    updated_at: datetime = field(default_factory=datetime.utcnow)

@dataclass
class PnLCalculation:
    """盈亏计算结果"""
    symbol: str
    exchange: str
    
    # 已实现盈亏
    realized_pnl: Decimal = Decimal('0')
    realized_pnl_today: Decimal = Decimal('0')
    
    # 未实现盈亏
    unrealized_pnl: Decimal = Decimal('0')
    
    # 总盈亏
    total_pnl: Decimal = Decimal('0')
    
    # 手续费
    commission: Decimal = Decimal('0')
    
    # 盈亏率
    pnl_ratio: Decimal = Decimal('0')
    
    # 计算时间
    calculated_at: datetime = field(default_factory=datetime.utcnow)

class PositionCalculator:
    """
    持仓计算器
    
    基于现有的OmsEngine架构设计
    提供实时持仓计算和PnL计算功能
    """
    
    def __init__(self):
        self.logger = logging.getLogger(__name__)
        
        # 持仓缓存
        self._position_cache: Dict[str, PositionData] = {}
        self._cache_expiry: Dict[str, datetime] = {}
        self._cache_timeout = timedelta(minutes=5)
        
        # 价格缓存
        self._price_cache: Dict[str, Decimal] = {}
        self._price_cache_expiry: Dict[str, datetime] = {}
        self._price_timeout = timedelta(seconds=30)
        
        # 合约信息缓存
        self._contract_cache: Dict[str, Any] = {}
        
        # 计算锁
        self._calculation_locks: Dict[str, asyncio.Lock] = {}
        
        self.logger.info("持仓计算器初始化完成")
    
    async def update_position(self, session: AsyncSession, user_id: str, trade: TradingTrade):
        """
        根据成交更新持仓
        
        Args:
            session: 数据库会话
            user_id: 用户ID
            trade: 成交记录
        """
        try:
            position_key = f"{user_id}:{trade.symbol}:{trade.exchange}"
            
            # 获取计算锁
            if position_key not in self._calculation_locks:
                self._calculation_locks[position_key] = asyncio.Lock()
            
            async with self._calculation_locks[position_key]:
                # 获取当前持仓
                position = await self._get_or_create_position(
                    session, user_id, trade.symbol, trade.exchange
                )
                
                # 计算新的持仓
                await self._calculate_position_change(position, trade)
                
                # 更新数据库
                await self._save_position(session, position)
                
                # 更新缓存
                self._position_cache[position_key] = position
                self._cache_expiry[position_key] = datetime.utcnow() + self._cache_timeout
                
                self.logger.debug(f"持仓更新完成: {position_key}")
                
        except Exception as e:
            self.logger.error(f"持仓更新失败: {e}")
            raise
    
    async def calculate_position_pnl(self, session: AsyncSession, user_id: str, 
                                   symbol: str, exchange: str,
                                   market_price: Optional[Decimal] = None) -> PnLCalculation:
        """
        计算持仓盈亏
        
        Args:
            session: 数据库会话
            user_id: 用户ID
            symbol: 证券代码
            exchange: 交易所
            market_price: 市场价格
            
        Returns:
            盈亏计算结果
        """
        try:
            # 获取持仓信息
            position = await self._get_position(session, user_id, symbol, exchange)
            if not position or position.volume == 0:
                return PnLCalculation(symbol=symbol, exchange=exchange)
            
            # 获取市场价格
            if market_price is None:
                market_price = await self._get_market_price(symbol, exchange)
            
            # 获取合约信息
            contract = await self._get_contract_info(session, symbol, exchange)
            if not contract:
                raise ContractNotFoundError(symbol, exchange)
            
            # 计算未实现盈亏
            unrealized_pnl = self._calculate_unrealized_pnl(
                position, market_price, contract.size
            )
            
            # 计算已实现盈亏
            realized_pnl, realized_pnl_today = await self._calculate_realized_pnl(
                session, user_id, symbol, exchange
            )
            
            # 计算手续费
            commission = await self._calculate_commission(
                session, user_id, symbol, exchange
            )
            
            # 计算总盈亏
            total_pnl = realized_pnl + unrealized_pnl
            
            # 计算盈亏率
            pnl_ratio = Decimal('0')
            if position.total_cost > 0:
                pnl_ratio = (total_pnl / position.total_cost * 100).quantize(
                    Decimal('0.01'), rounding=ROUND_HALF_UP
                )
            
            return PnLCalculation(
                symbol=symbol,
                exchange=exchange,
                realized_pnl=realized_pnl,
                realized_pnl_today=realized_pnl_today,
                unrealized_pnl=unrealized_pnl,
                total_pnl=total_pnl,
                commission=commission,
                pnl_ratio=pnl_ratio
            )
            
        except Exception as e:
            self.logger.error(f"盈亏计算失败: {e}")
            raise
    
    async def calculate_user_total_pnl(self, session: AsyncSession, user_id: str) -> Dict[str, Decimal]:
        """
        计算用户总盈亏
        
        Args:
            session: 数据库会话
            user_id: 用户ID
            
        Returns:
            总盈亏信息
        """
        try:
            # 获取用户所有持仓
            positions = await self._get_user_positions(session, user_id)
            
            total_realized_pnl = Decimal('0')
            total_unrealized_pnl = Decimal('0')
            total_commission = Decimal('0')
            
            # 计算每个持仓的盈亏
            for position in positions:
                if position.volume > 0:
                    pnl_calc = await self.calculate_position_pnl(
                        session, user_id, position.symbol, position.exchange
                    )
                    
                    total_realized_pnl += pnl_calc.realized_pnl
                    total_unrealized_pnl += pnl_calc.unrealized_pnl
                    total_commission += pnl_calc.commission
            
            return {
                "realized_pnl": total_realized_pnl,
                "unrealized_pnl": total_unrealized_pnl,
                "total_pnl": total_realized_pnl + total_unrealized_pnl,
                "commission": total_commission
            }
            
        except Exception as e:
            self.logger.error(f"用户总盈亏计算失败: {e}")
            raise
    
    async def calculate_risk_metrics(self, session: AsyncSession, user_id: str) -> Dict[str, Any]:
        """
        计算风险指标
        
        Args:
            session: 数据库会话
            user_id: 用户ID
            
        Returns:
            风险指标
        """
        try:
            # 获取用户资金账户
            accounts = await self._get_user_accounts(session, user_id)
            total_balance = sum(acc.balance for acc in accounts)
            total_available = sum(acc.available for acc in accounts)
            
            # 获取用户持仓
            positions = await self._get_user_positions(session, user_id)
            total_position_value = Decimal('0')
            max_single_position = Decimal('0')
            
            for position in positions:
                if position.volume > 0:
                    market_price = await self._get_market_price(position.symbol, position.exchange)
                    position_value = position.volume * market_price
                    total_position_value += position_value
                    max_single_position = max(max_single_position, position_value)
            
            # 计算风险指标
            position_ratio = Decimal('0')
            if total_balance > 0:
                position_ratio = (total_position_value / total_balance * 100).quantize(
                    Decimal('0.01'), rounding=ROUND_HALF_UP
                )
            
            max_position_ratio = Decimal('0')
            if total_balance > 0:
                max_position_ratio = (max_single_position / total_balance * 100).quantize(
                    Decimal('0.01'), rounding=ROUND_HALF_UP
                )
            
            # 计算资金使用率
            fund_usage_ratio = Decimal('0')
            if total_balance > 0:
                fund_usage_ratio = ((total_balance - total_available) / total_balance * 100).quantize(
                    Decimal('0.01'), rounding=ROUND_HALF_UP
                )
            
            # 计算盈亏信息
            pnl_info = await self.calculate_user_total_pnl(session, user_id)
            
            # 确定风险等级
            risk_level = self._determine_risk_level(
                position_ratio, max_position_ratio, fund_usage_ratio
            )
            
            return {
                "total_balance": total_balance,
                "total_available": total_available,
                "total_position_value": total_position_value,
                "position_ratio": position_ratio,
                "max_position_ratio": max_position_ratio,
                "fund_usage_ratio": fund_usage_ratio,
                "risk_level": risk_level,
                "total_pnl": pnl_info["total_pnl"],
                "pnl_ratio": (pnl_info["total_pnl"] / max(total_balance, 1) * 100).quantize(
                    Decimal('0.01'), rounding=ROUND_HALF_UP
                )
            }
            
        except Exception as e:
            self.logger.error(f"风险指标计算失败: {e}")
            raise
    
    async def _get_or_create_position(self, session: AsyncSession, user_id: str, 
                                    symbol: str, exchange: str) -> PositionData:
        """获取或创建持仓"""
        position_key = f"{user_id}:{symbol}:{exchange}"
        
        # 检查缓存
        if (position_key in self._position_cache and 
            position_key in self._cache_expiry and
            self._cache_expiry[position_key] > datetime.utcnow()):
            return self._position_cache[position_key]
        
        # 从数据库查询
        db_position = await session.scalar(
            select(TradingPosition).where(and_(
                TradingPosition.user_id == user_id,
                TradingPosition.symbol == symbol,
                TradingPosition.exchange == exchange
            ))
        )
        
        if db_position:
            position = PositionData(
                user_id=user_id,
                symbol=symbol,
                exchange=exchange,
                direction=db_position.direction,
                volume=db_position.volume,
                frozen=db_position.frozen,
                average_price=db_position.price,
                yd_volume=db_position.yd_volume
            )
            position.available = position.volume - position.frozen
            position.total_cost = position.volume * position.average_price
        else:
            # 创建新持仓
            position = PositionData(
                user_id=user_id,
                symbol=symbol,
                exchange=exchange,
                direction=PositionDirection.NET
            )
        
        return position
    
    async def _get_position(self, session: AsyncSession, user_id: str, 
                          symbol: str, exchange: str) -> Optional[PositionData]:
        """获取持仓"""
        try:
            return await self._get_or_create_position(session, user_id, symbol, exchange)
        except Exception:
            return None
    
    async def _calculate_position_change(self, position: PositionData, trade: TradingTrade):
        """计算持仓变化"""
        trade_volume = trade.volume
        trade_price = trade.price
        
        # 根据交易方向更新持仓
        if trade.direction == TradeDirection.BUY:
            # 买入增加持仓
            old_cost = position.volume * position.average_price
            new_cost = trade_volume * trade_price
            new_volume = position.volume + trade_volume
            
            if new_volume > 0:
                position.average_price = (old_cost + new_cost) / new_volume
            position.volume = new_volume
            
        else:  # TradeDirection.SELL
            # 卖出减少持仓
            position.volume = max(Decimal('0'), position.volume - trade_volume)
            
            # 如果全部卖出，重置均价
            if position.volume == 0:
                position.average_price = Decimal('0')
        
        # 更新可用数量
        position.available = position.volume - position.frozen
        position.total_cost = position.volume * position.average_price
        position.updated_at = datetime.utcnow()
    
    async def _save_position(self, session: AsyncSession, position: PositionData):
        """保存持仓到数据库"""
        # 查找现有持仓记录
        db_position = await session.scalar(
            select(TradingPosition).where(and_(
                TradingPosition.user_id == position.user_id,
                TradingPosition.symbol == position.symbol,
                TradingPosition.exchange == position.exchange
            ))
        )
        
        if db_position:
            # 更新现有记录
            db_position.volume = position.volume
            db_position.frozen = position.frozen
            db_position.price = position.average_price
            db_position.pnl = position.pnl
            db_position.updated_at = position.updated_at
        else:
            # 创建新记录
            db_position = TradingPosition(
                id=str(__import__('uuid').uuid4()),
                user_id=position.user_id,
                symbol=position.symbol,
                exchange=position.exchange,
                direction=position.direction,
                volume=position.volume,
                frozen=position.frozen,
                price=position.average_price,
                pnl=position.pnl
            )
            session.add(db_position)
        
        await session.commit()
    
    async def _get_market_price(self, symbol: str, exchange: str) -> Decimal:
        """获取市场价格"""
        price_key = f"{symbol}:{exchange}"
        
        # 检查价格缓存
        if (price_key in self._price_cache and 
            price_key in self._price_cache_expiry and
            self._price_cache_expiry[price_key] > datetime.utcnow()):
            return self._price_cache[price_key]
        
        # 这里应该从行情服务获取实时价格
        # 暂时返回模拟价格
        mock_price = Decimal('10.50')  # 模拟价格
        
        # 缓存价格
        self._price_cache[price_key] = mock_price
        self._price_cache_expiry[price_key] = datetime.utcnow() + self._price_timeout
        
        return mock_price
    
    async def _get_contract_info(self, session: AsyncSession, symbol: str, exchange: str):
        """获取合约信息"""
        contract_key = f"{symbol}:{exchange}"
        
        # 检查缓存
        if contract_key in self._contract_cache:
            return self._contract_cache[contract_key]
        
        # 从数据库查询
        contract = await session.scalar(
            select(TradingContract).where(and_(
                TradingContract.symbol == symbol,
                TradingContract.exchange == exchange
            ))
        )
        
        if contract:
            self._contract_cache[contract_key] = contract
        
        return contract
    
    def _calculate_unrealized_pnl(self, position: PositionData, market_price: Decimal, 
                                contract_size: Decimal) -> Decimal:
        """计算未实现盈亏"""
        if position.volume == 0:
            return Decimal('0')
        
        # 计算盈亏
        price_diff = market_price - position.average_price
        pnl = price_diff * position.volume * contract_size
        
        return pnl.quantize(Decimal('0.01'), rounding=ROUND_HALF_UP)
    
    async def _calculate_realized_pnl(self, session: AsyncSession, user_id: str, 
                                    symbol: str, exchange: str) -> Tuple[Decimal, Decimal]:
        """计算已实现盈亏"""
        # 查询所有平仓成交
        today = datetime.now().date()
        
        # 总已实现盈亏（简化计算，实际应该根据开平仓匹配）
        realized_query = select(
            func.sum(TradingTrade.turnover).label('total_turnover'),
            func.sum(TradingTrade.commission).label('total_commission')
        ).where(and_(
            TradingTrade.user_id == user_id,
            TradingTrade.symbol == symbol,
            TradingTrade.exchange == exchange,
            TradingTrade.direction == TradeDirection.SELL
        ))
        
        result = await session.execute(realized_query)
        row = result.first()
        
        realized_pnl = (row.total_turnover or Decimal('0')) - (row.total_commission or Decimal('0'))
        
        # 当日已实现盈亏
        today_query = select(
            func.sum(TradingTrade.turnover).label('today_turnover'),
            func.sum(TradingTrade.commission).label('today_commission')
        ).where(and_(
            TradingTrade.user_id == user_id,
            TradingTrade.symbol == symbol,
            TradingTrade.exchange == exchange,
            TradingTrade.direction == TradeDirection.SELL,
            func.date(TradingTrade.trade_time) == today
        ))
        
        today_result = await session.execute(today_query)
        today_row = today_result.first()
        
        realized_pnl_today = (today_row.today_turnover or Decimal('0')) - (today_row.today_commission or Decimal('0'))
        
        return realized_pnl, realized_pnl_today
    
    async def _calculate_commission(self, session: AsyncSession, user_id: str, 
                                  symbol: str, exchange: str) -> Decimal:
        """计算手续费"""
        commission_query = select(
            func.sum(TradingTrade.commission)
        ).where(and_(
            TradingTrade.user_id == user_id,
            TradingTrade.symbol == symbol,
            TradingTrade.exchange == exchange
        ))
        
        result = await session.scalar(commission_query)
        return result or Decimal('0')
    
    async def _get_user_positions(self, session: AsyncSession, user_id: str) -> List[TradingPosition]:
        """获取用户所有持仓"""
        query = select(TradingPosition).where(and_(
            TradingPosition.user_id == user_id,
            TradingPosition.volume > 0
        ))
        
        result = await session.execute(query)
        return result.scalars().all()
    
    async def _get_user_accounts(self, session: AsyncSession, user_id: str) -> List[TradingAccount]:
        """获取用户资金账户"""
        query = select(TradingAccount).where(TradingAccount.user_id == user_id)
        result = await session.execute(query)
        return result.scalars().all()
    
    def _determine_risk_level(self, position_ratio: Decimal, max_position_ratio: Decimal, 
                            fund_usage_ratio: Decimal) -> str:
        """确定风险等级"""
        if position_ratio > 80 or max_position_ratio > 50 or fund_usage_ratio > 90:
            return "HIGH"
        elif position_ratio > 60 or max_position_ratio > 30 or fund_usage_ratio > 70:
            return "MEDIUM"
        elif position_ratio > 40 or max_position_ratio > 20 or fund_usage_ratio > 50:
            return "LOW"
        else:
            return "MINIMAL"
    
    def clear_cache(self):
        """清理缓存"""
        now = datetime.utcnow()
        
        # 清理过期的持仓缓存
        expired_positions = [
            key for key, expiry in self._cache_expiry.items()
            if expiry <= now
        ]
        for key in expired_positions:
            self._position_cache.pop(key, None)
            self._cache_expiry.pop(key, None)
        
        # 清理过期的价格缓存
        expired_prices = [
            key for key, expiry in self._price_cache_expiry.items()
            if expiry <= now
        ]
        for key in expired_prices:
            self._price_cache.pop(key, None)
            self._price_cache_expiry.pop(key, None)
    
    def get_cache_stats(self) -> Dict[str, int]:
        """获取缓存统计"""
        return {
            "position_cache_size": len(self._position_cache),
            "price_cache_size": len(self._price_cache),
            "contract_cache_size": len(self._contract_cache),
            "active_locks": len(self._calculation_locks)
        }